#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>

#include "switch.h"

#define CNAME "switch"

int major = 0;
int minor = 0;

struct cdev   *cdev;
struct class  *cls;
struct device *dev;

struct device_node *node;
int gpiono[3];
const char *name[] = {"led1","fan","motor"};

// control_device{
// 		led1 = <&gpioe 10 0>;
// 		led2 = <&gpiof 10 0>;
// 		led3 = <&gpioe 8 0>;
// 		buzzer = <&gpiob 6 0>;
// 		fan = <&gpioe 9 0>;
// 		motor = <&gpiof 6 0>;
// 	};

//交互函数
int mycdev_open (struct inode *inode, struct file *file){
    printk("switch_open\n");
    return 0;
}
long mycdev_ioctl (struct file *file, unsigned int cmd, unsigned long arg){

    int which, ret;
    ret = copy_from_user(&which, (void *)arg, _IOC_SIZE(cmd));
    if (ret){
        printk("copy_from_user error\n");
        return -EIO;
    }
    //printk("which = %d\n", which);
    switch (cmd){
        case SWITCH_ON:
            gpio_set_value(gpiono[which], ON);
            break;
        case SWITCH_OFF:
            gpio_set_value(gpiono[which], OFF);
            break;
    }
    return 0;
}
int mycdev_close (struct inode *inode, struct file *file){
    printk("switch_close\n");
    return 0;
}

//交互函数结构体
const struct file_operations fops = {
    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close
};

int switch_init(void){

    int i, ret;
    // 1.获取节点
    node = of_find_node_by_path("/control_device");
    if(node == NULL){
        printk("of_find_node_by_path error\n");
        return -ENODATA;
    }
    // 2.解析得到gpio号
    for (i = 0; i < ARRAY_SIZE(name); i++){
        gpiono[i] = of_get_named_gpio(node, name[i],0);
        if(gpiono[i] < 0){
            printk("of_get_named_gpio error\n");
            ret = gpiono[i];
            goto err1;
        }
        // 3.申请要使用的gpio
        ret = gpio_request(gpiono[i],NULL);
        if(ret){
            printk("gpio_request error：%d\n", ret);
            goto err1;
        }
        // 4.设置方向
        gpio_direction_output(gpiono[i],0);
    }
    //printk("gpio_set_value open suc\n");
    return 0;
err1:
    for (--i; i >= 0; i--) {
        gpio_free(gpiono[i]);
    }
    return ret;
}
void switch_exit(void){
    int i;
    for (i = 0; i < ARRAY_SIZE(name); i++){
        gpio_set_value(gpiono[i],0);
        gpio_free(gpiono[i]);
    }
}
static int __init mycdev_init(void)
{   
    
    dev_t devno;
    int ret;
    //1、分配对象
    cdev = cdev_alloc();
    if (cdev == NULL){
        printk("cdev_alloc error");
        goto err1;
    }
    //2、对象初始化
    cdev_init(cdev, &fops);
    //3、申请设备号
    ret = alloc_chrdev_region(&devno, 0, 1, CNAME);
    if (ret){
        printk("alloc_chrdev_region error\n");
        goto err2;
    }
    major = MAJOR(devno);
    minor = MINOR(devno);
    //4、注册字符设备驱动
    ret = cdev_add(cdev, MKDEV(major, minor), 1);
    if (ret){
        printk("cdev_add error\n");
        goto err3;
    }
    //5、自动创建设备节点
    cls = class_create(THIS_MODULE, CNAME);
    if (IS_ERR(cls)){
        printk("class_create error\n");
        goto err4;
    }
    dev = device_create(cls, NULL, MKDEV(major, minor), NULL, CNAME);
    if (IS_ERR(dev)){
        printk("device_create error\n");
        goto err5; 
    }

    /*灯的初始化*/
    if (switch_init()){
        printk("switch_init error\n");
        goto err6;
    }

    return 0;

err6:
    device_destroy(cls, MKDEV(major, minor));
err5:
    class_destroy(cls);
err4:
    cdev_del(cdev);
err3:
    unregister_chrdev_region(MKDEV(major, minor), 1);
err2:
    kfree(cdev);
err1:
    return ret;
}
static void __exit mycdev_exit(void)
{    
    //倒叙销毁
    switch_exit();
    device_destroy(cls, MKDEV(major, minor));
    class_destroy(cls);
    cdev_del(cdev);
    unregister_chrdev_region(MKDEV(major, minor), 1);
    kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");